// locutor
// (c) 2008-2009, Normen Müller, normen.mueller@gmail.com  
// $Id$
package locutor.model.parsing.grgen

import collection.mutable.HashSet

/** 
 * @author <a href="mailto:normen.mueller@googlemail.com">Normen M&#xFC;ller</a>
 * @version 0.0.2 */
private [grgen] object ExprParser {
  
  val reserved = new HashSet[String] ++ List(
    "int", "double", "float", "string",
    "true", "false", "null",
    "visited", "nameof", "typeof"    
  )
  
  val delimiters = new HashSet[String] ++ List(
    "[", "]", "(", ")",
    "!", "?", ":", ",", ".", "::", 
    "^", "&&", "||", "&", "|",
    "<<", ">>>", ">>", "+", "-", "*", "/", "%", "~",
    "<=", "<", "==", "!=", ">=", ">"    
  )
  
}

/** Primitive GrGEN expression parser.
 *
 * At the moment only primitive string representations of GrGEN expressions are supported.
 * No need for sophisticated AST yet.
 * 
 * Parser restrictions:
 * - Keyword ``in'' not yet supported
 * 
 * @author <a href="mailto:normen.mueller@googlemail.com">Normen M&#xFC;ller</a>
 * @version 0.0.2 */
private [grgen] trait ExprParser extends BaseParser {
  
  def expr: Parser[String] = cond

  def cond: Parser[String] = lor ~ opt("?" ~ expr ~ ":" ~ cond) ^^ {
    case expr ~ exprs => ((new StringBuilder(expr) /: exprs)((b, ms) => ms match {case o ~ x => b append o append x})).toString
  }

  def lor = land ~ (("||" ~ land)*) ^^ {
    case expr ~ exprs => ((new StringBuilder(expr) /: exprs)((b, ms) => ms match {case o ~ x => b append o append x})).toString
  }

  def land = bor ~ (("&&" ~ bor)*)  ^^ {
    case expr ~ exprs => ((new StringBuilder(expr) /: exprs)((b, ms) => ms match {case o ~ x => b append o append x})).toString
  }

  def bor = bxor ~ (("|" ~ bxor)*) ^^ {
    case expr ~ exprs => ((new StringBuilder(expr) /: exprs)((b, ms) => ms match {case o ~ x => b append o append x})).toString
  }

  def bxor = band ~ (("^" ~ band)*) ^^ {
    case expr ~ exprs => ((new StringBuilder(expr) /: exprs)((b, ms) => ms match {case o ~ x => b append o append x})).toString
  }

  def band = except ~ (("&" ~ except)*) ^^ {
    case expr ~ exprs => ((new StringBuilder(expr) /: exprs)((b, ms) => ms match {case o ~ x => b append o append x})).toString
  }

  def except = eq_expr ~ (("\\" ~ eq_expr)*)  ^^ {
    case expr ~ exprs => ((new StringBuilder(expr) /: exprs)((b, ms) => ms match {case o ~ x => b append o append x})).toString
  }

  def eq_expr = rel ~ ((("==" | "!=") ~ rel)*)  ^^ {
    case expr ~ exprs => ((new StringBuilder(expr) /: exprs)((b, ms) => ms match {case o ~ x => b append o append x})).toString
  }

  def rel = shift ~ ((("<=" | "<" | ">=" | ">") ~ shift)*)  ^^ {
    case expr ~ exprs => ((new StringBuilder(expr) /: exprs)((b, ms) => ms match {case o ~ x => b append o append x})).toString
  }

  def shift = add ~ ((("<<" | ">>>" | ">>") ~ add)*) ^^ {
    case expr ~ exprs => ((new StringBuilder(expr) /: exprs)((b, ms) => ms match {case o ~ x => b append o append x})).toString
  }

  def add = mul ~ ((("+" | "-") ~ mul)*) ^^ {
    case expr ~ exprs => ((new StringBuilder(expr) /: exprs)((b, ms) => ms match {case o ~ x => b append o append x})).toString
  }

  def mul = unary ~ ((("*" | "%" | "/") ~ unary)*) ^^ {
    case expr ~ exprs => ((new StringBuilder(expr) /: exprs)((b, ms) => ms match {case o ~ x => b append o append x})).toString
  }

  def unary: Parser[String] =
    ( ("~" | "!" | "-" | "+") ~ unary       ^^ {case op ~ expr  => op + expr}
    | ("(" ~> primitives) ~ (")" ~> unary)  ^^ {case t ~ expr   => "(" + t + ")" + expr}
    | (primary ~ ((selector)*))             ^^ {case expr ~ sel => expr + (sel mkString)}
    )

  def primary =
    ( visited | nameOf | typeOf  | constant
    | (ident <~ "::") ~ ident ^^ {case l ~ r => l + "::" + r}
    | ident    
    | "(" ~> expr <~ ")"      ^^ {case expr => "(" + expr + ")"}
    )

  def visited =
    ( ("visited" ~ "(") ~> ident <~ ")"                   ^^ {case id        => "visited(" + id + ")"}
    | ("visited" ~ "(") ~> (ident <~ ",") ~ (expr <~ ")") ^^ {case id ~ expr => "visisted(" + id + ", " + expr + ")"}
    )

  def nameOf = ("nameof" ~ "(") ~> ident <~ ")" ^^ {case id => "nameof(" + id + ")"}

  def typeOf = ("typeof" ~ "(") ~> ident <~ ")" ^^ {case id => "typeof(" + id + ")"}

  private def selector =
    ( "[" ~> expr <~ "]"        ^^ { case expr => "[" + expr + "]"}
    | "." ~ ident ~ opt(params) ^^ {
      case dot ~ id ~ Some(params) => dot + id + params
      case dot ~ id ~ None         => dot + id
    }
    )

  private def params = "(" ~> opt(expr ~ (("," ~> expr)*)) <~ ")" ^^ {
    case Some(expr ~ exprs) => "(" + expr + ", " + (exprs mkString ", ") + ")"
    case None               => "()"
  }

}
